package com.huanghwh.sync.core.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.ds.DSFactory;
import com.huanghwh.sync.core.entity.SyncLog;
import com.huanghwh.sync.core.entity.SyncResult;
import com.huanghwh.sync.core.service.ILogService;
import com.huanghwh.sync.core.service.ISyncCoreService;
import com.huanghwh.sync.message.service.IMessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Objects;

/**
 * DB同步核心实现类
 * @Author: huanghwh
 * @Date: 2021/12/21 14:16
 * @Description:
 */
@Slf4j
@Service
public class SyncCoreServiceImpl implements ISyncCoreService {

    @Autowired
    private IMessageService messageService;
    @Autowired
    private ILogService logService;

    /**
     * 执行同步
     *
     * @param originDbConfigName
     * @param targetDbConfigName
     * @param tableName
     * @return
     * @Author huanghwh
     * @Date 2021/12/21 14:29
     */
    @Override
    public SyncResult executeSync(String originDbConfigName, String targetDbConfigName, String tableName, Integer minute) {
        int total = 0;
        int failNum = 0;
        List<Entity> failList = new ArrayList<>();
        DataSource originDbSource = null;
        DataSource targetDbSource = null;
        if (Objects.isNull(minute)) {
            throw new NullPointerException("minute must not null");
        }
        try {
            originDbSource = DSFactory.get(originDbConfigName);
            targetDbSource = DSFactory.get(targetDbConfigName);
            // 查询指定范围的数据
            StringBuilder sql = new StringBuilder();
            sql.append("select * from ");
            sql.append(tableName);
            sql.append(" where to_char(CREATE_TIME, 'yyyy-mm-dd hh24:mi') >= ?");
            // 构造时间条件
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.MINUTE, minute);
            SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
            // 执行查询
            log.info("SQL: {}, {}", sql, sf.format(calendar.getTime()));
            List<Entity> entityList = Db.use(originDbSource).query(sql.toString(), sf.format(calendar.getTime()));
            Db targetDb = Db.use(targetDbSource);
            for (Entity entity : entityList) {
                entity.setTableName(tableName);
                total ++;
                try {
                    targetDb.insert(entity);
                } catch (Exception e) {
                    failNum ++;
                    log.error("Table：{}，同步失败，原因：{}，{}", tableName, e, entity);
                    // 推送通知
                    String errorStr = "Table：{0}，同步失败，原因：{1}，UUID: {2}";
                    String UUID = String.valueOf(entity.get("UUID"));
                    String errorMsg = MessageFormat.format(errorStr, tableName, e, UUID);
                    messageService.sendMessageToBark(errorMsg);
                    // 记录同步错误的记录
                    Entity logEntity = SyncLog.createLogEntity(UUID, tableName, e.toString());
                    failList.add(logEntity);
                }
            }
        } catch (SQLException e) {
            // 记录异常，处理失败的数据
            log.error("数据库表同步出现异常({})： {}",  tableName, e.toString());
        } finally {
            try {
                if (Objects.nonNull(originDbSource)) {
                    originDbSource.getConnection().close();
                }
                if (Objects.nonNull(targetDbSource)) {
                    targetDbSource.getConnection().close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            // 保存到日志表
            if (CollectionUtil.isNotEmpty(failList)) {
                logService.save(failList);
            }
        }
        SyncResult syncResult = new SyncResult();
        syncResult.setTotal(total);
        syncResult.setSuccessNum(total - failNum);
        syncResult.setFailNum(failNum);
        return syncResult;
    }


}
